const AppError = require("../AppError")

const ParamType = {
    STR : "string" ,
    // BOOL : "boolean"  ,
    Number : "number" ,
    // Pan
    Pag : "pag"
}

/**
 * 
 *  Rule row example.
 *      {
 *          name : string   //对应字段名和转换后的名称
 *          detail : string //中文说明
 *          fname : string  //默认为空,不为空时为转换名
 *          default : string  //.
 *          from : string   // method:随请求类型获取 header:请求头 body:请求体 query:请求URL cookie:cookie内
 *          isNull : boolean    //是否允许为空，默认false
 *          type : string   // 枚举 String/Number/Boolean/Float  不区分大小写
 *          min : 最小值
 *          max : 最大值
 *          after : fn        //预处理  function( val , rule )
 *      }
 * 
 */
class ParamOption {

    constructor( obj ){
        this.name = obj.name 
        this.detail = obj.detail || obj.name 
        this.fname = obj.fname || obj.name 
        this.default = obj.default
        this.from = (obj.from || "method").toLowerCase()
        this.isNull = obj.isNull ? obj.isNull : false //是否为空,默认为false 不允许
        this.type = obj.type ? obj.type : ParamType.STR    //默认为string
        this.min = obj.min ? obj.min : 0    // >= min
        this.max = obj.size ? obj.size : 1024*1024   //default 1024    
        this.after = obj.after    //默认不调用
    }

    static Type( obj ){
        return new ParamOption( obj )
    }

    static String( name , detail , min , max , isNull ) {
        return new ParamOption({
            name : name ,
            detail : detail ,
            min : min , 
            size : max ,
            isNull : isNull ? isNull : false 
        })
    }

    static Number( name , detail , isNull  , defaultValue ){
        return new ParamOption({
            name : name , 
            detail : detail ,
            type : ParamType.Number ,
            default : defaultValue ,
            isNull : isNull ? isNull : false  
        })
    }

    static Pag(){
        return new ParamOption({
            type : ParamType.Pag
        })
    }

    transfer( val ){
        val = val + ""
        if( this.type == ParamType.Number ){
            if( !isNaN( val ) ){
                val = parseInt( val )
            }else{
                throw new AppError(21 , this.detail + "的值必须为数字" )
            }
        }
        return val 
    }

    get( val ){
        if( !val || val == "undefined"){
            if( !this.isNull ){
                //不允许为空
                if( this.default )  return this.default 
                else    throw AppError.checkError( 22 , this.detail + "的值不允许为空" )
            }else{
                return null 
            }
        }

        if( this.type == ParamType.STR ){

            const len = (val+"").length 
            if( !( (this.max >= len) && ( len >= this.min) )) {
                throw AppError.checkError( 23 , this.detail + "值的长度必须大于" + this.min + "并且小于" + this.max )
            }
        }

        val = this.transfer( val )

        if( this.type == ParamType.Number ){
            if( !(( this.max >= val ) && ( val >= this.min )) )
                throw AppError.checkError(24 , this.detail + " 的值必须大于" + this.min + "并且小于" + this.max  )    
        }
        return this.format( val )

    }

    format( val ){
        if( this.after && typeof( this.after ) == "function" )
            return this.after( val )
        else
            return val
    }

}

class RequestParser {

    constructor(){

    }

    getRule( ctx ){
        const path = ctx.request.path.split("?")[0]
        const method = ctx.request.method 
        const uri = method.toUpperCase() + " " + path 

        const router = ctx.app._Router[ uri ]
        if( router ){
            const rules = router["params"]
            return rules?rules: []
        }
        return null 
    }   

    values( ctx ){
        const rules = this.getRule( ctx  ) 
        if( !rules )
            throw new AppError( 81 , "无此路由: " + ctx.request.path.split("?")[0]  )
        
        if( rules.length >= 0 ){
            return this.parse( rules , ctx )
        }
    }

    parsePag( query ){
        let pag = {}
        let index = query["index"] || 1
        const size = query["size"] || 30 
        const limit = size>30||size<=0 ? 30 : size 
        index = (index <= 0 ) ? 1 : index 
        const offset = ( index - 1 ) * limit 
        pag["offset"] = parseInt( offset )
        pag["limit"] = parseInt(  limit )
        return pag
    }

    parse( rules , ctx ){
        let parserBody = {}
        parserBody.ps = {}
        //兼容不同请求类型的参数数据源
        let query = Object.assign( ctx.request.query || {} ,
                ctx.request.body || {})
        // if( ctx.request.method == "GET")
        //     query = ctx.request.query 
        // else
        //     query = ctx.request.body 
        for( let ruleIndex in rules ){
            const pr = rules[ ruleIndex ] 
            if( pr.type == ParamType.Pag ){
                parserBody.pag = this.parsePag( query )
                //分页数据单独处理 
            }else{
                const key = pr.fname 
                let value 
                if( this.from == "query"){
                    value = ctx.request.query[ key ]
                }else if( this.from == "body"){
                    value = ctx.request.body[ key ]
                }else if( this.from == "header"){
                    value = ctx.request.headers[ key ]
                }else if( this.from == "cookie"){
                    value = ctx.cookies.get( key )
                }else{
                    value = query[ key ]
                }
                value = pr.get( value )
                if( value )
                    parserBody.ps[ key ] = value 
            }
        }
        return parserBody 
    }

}

module.exports = {
    RequestParser , ParamOption , ParamType 
}